package com.witframework.plugin.lucene.resource.impl;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Fieldable;

import com.witframework.plugin.lucene.ResourceException;
import com.witframework.plugin.lucene.builder.DocumentBuilder;
import com.witframework.plugin.lucene.builder.impl.ContentsDocumentBuilder;
import com.witframework.plugin.lucene.resource.ResourceFieldMetaData;
import com.witframework.plugin.lucene.resource.ResourceMetaData;
import com.witframework.util.BeanUtils;
import com.witframework.xml.XMLUtil;

class BeanDocumentHandler {

	private static final Log log = LogFactory	.getLog(BeanDocumentHandler.class);

	/**
	 * 生成Document对象，根据元数据为bean建立索引<P>
	 * 注意该方法不处理bean的byte[]类型的属性。因为这个方法认为byte[]类型的属性的值为
	 * 数据附件。因此需要手工构建对附件的索引，对于包含附件的数据，
	 * 可通过如下示例方法建立索引
	 *  <blockquote>example:<pre>	 *
	 *  DefaultDataBaseResource dbr=new DefaultDataBaseResource("resourceName");
	 *  dbr.setResourceProcessor(resourceProcessor);
	 *  Document doc=createDocument(bean);//按照元数据建立索引
	 *  String contents=wordTextBuilder.getText(is);//is为附件的输入流
	 *  //加入对附件内容的处理
	 *  doc.add(new Field("contents",contents,Field.Store.YES,Field.Index.TOKENIZE));
	 *  doc.add(new Field("id",bean.getId(),Field.Store.YES,Field.Index.TOKENIZE);
	 *  dbr.addDocument(doc);
	 *  dbr.extractDocuments();//建立索引
	 * @param bean
	 * @return -Document对象
	 */
	public Document createDocument(Object bean,ResourceMetaData rmd) throws ResourceException{
		//将java bean转换为XML字符串，并将其内容作为索引的摘要信息保存。
		//对于id内容和byte[]类型的属性不进行索引和存储。
		String id = null;
		try {
			id = (String) BeanUtils.getDeclaredProperty(bean, "id");
			BeanUtils.setDeclaredProperty(bean, "id", null);
		} catch (Exception e) {
			log.debug("can get id value.", e);
		}
		List<java.lang.reflect.Field> fs = BeanUtils.getFieldsByType(bean,
				byte[].class);
		for (java.lang.reflect.Field f : fs) {
			try {
				BeanUtils.setDeclaredProperty(bean, f.getName(), null);
			} catch (Exception e) {
				throw new ResourceException(
						"can not set byte[] fields to null.", e);
			}
		}
		DocumentBuilder adb = new ContentsDocumentBuilder(XMLUtil.beanToXml(bean));
		Document doc = adb.createDocument(null);
		log.debug("Index:::contents:"+doc.get("contents"));
		try {
			if (id != null) {// 加入名称为“id”的Field
				doc.add(new Field("id", id, Field.Store.YES, Field.Index.NO));
			}
		} catch (Exception e) {
			throw new ResourceException("can not set id value.", e);
		}
		Field.Index indexMode = Field.Index.TOKENIZED;
		Field.Store storeMode = Field.Store.YES;
		if (rmd != null) {
			//如果有元数据，按照元数据进行索引
			ResourceFieldMetaData[] rfmds = rmd.getResourceFieldMetaData();
			for (int i = 0; i < rfmds.length; i++) {
				indexMode = getIndexMode(rfmds[i].getIndex());
				storeMode = getStoreMode(rfmds[i].getStore());
				if (storeMode != Field.Store.NO || indexMode != Field.Index.NO) {
					String name = rfmds[i].getCode();
					String value;
					try {
						value = (String) BeanUtils.getDeclaredProperty(bean,name);
					} catch (Exception e) {
						throw new ResourceException("can not get field value of bean.", e);
					}

					Fieldable fieldable=new Field(name, value, storeMode, indexMode);
					fieldable.setBoost(rfmds[i].getBoost());
					doc.add(fieldable);
					log.debug("Index:::field:"+doc.get(name));
				}
			}
			doc.add(new Field("name", rmd.getName(), Field.Store.YES,Field.Index.NO));
			doc.add(new Field("hitPattern", rmd.getHitPattern(), Field.Store.YES,Field.Index.NO));
			if(rmd.getRType()!=null)
				doc.add(new Field("rType", "db",Field.Store.YES,Field.Index.NO));
		}else{
			doc.add(new Field("name",rmd.getName(), Field.Store.YES,Field.Index.NO));
		}
		return doc;
	}
	private Field.Index getIndexMode(String mode) {
		mode = mode.toUpperCase();
		if (mode.equals("UN_TOKENIZED"))
			return Field.Index.UN_TOKENIZED;
		if (mode.equals("NO"))
			return Field.Index.NO;
		if (mode.equals("NO_NORMS"))
			return Field.Index.NO_NORMS;
		return Field.Index.TOKENIZED;
	}

	private Field.Store getStoreMode(String mode) {
		mode = mode.toUpperCase();
		if (mode.equals("NO"))
			return Field.Store.NO;
		if (mode.equals("COMPRESS"))
			return Field.Store.COMPRESS;
		return Field.Store.YES;
	}
}
